﻿#region Using Statements

using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;
using System.Web;

#endregion


//http://weblogs.asp.net/rashid/archive/2009/02/15/asp-net-mvc-unity-and-common-service-locator.aspx

namespace Merbla.Coffee.Web.Core
{
    public class PerRequestLifetimeManager : LifetimeManager
    {
        private HttpContextBase _httpContext;

        //Need this constructor for Unit Test
        public PerRequestLifetimeManager(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public PerRequestLifetimeManager()
            : this(new HttpContextWrapper(HttpContext.Current))
        {
        }

        private IDictionary<PerRequestLifetimeManager, object> BackingStore
        {
            get
            {
                _httpContext = (HttpContext.Current != null)
                                   ? new HttpContextWrapper(HttpContext.Current)
                                   : _httpContext;

                return PerRequestLifetimeModule.GetInstances(_httpContext);
            }
        }

        private object Value
        {
            get
            {
                IDictionary<PerRequestLifetimeManager, object> backingStore = BackingStore;

                return backingStore.ContainsKey(this) ? backingStore[this] : null;
            }
            set
            {
                IDictionary<PerRequestLifetimeManager, object> backingStore = BackingStore;

                if (backingStore.ContainsKey(this))
                {
                    object oldValue = backingStore[this];

                    if (!ReferenceEquals(value, oldValue))
                    {
                        var disposable = oldValue as IDisposable;

                        if (disposable != null)
                        {
                            disposable.Dispose();
                        }

                        if (value == null)
                        {
                            backingStore.Remove(this);
                        }
                        else
                        {
                            backingStore[this] = value;
                        }
                    }
                }
                else
                {
                    if (value != null)
                    {
                        backingStore.Add(this, value);
                    }
                }
            }
        }

        public override object GetValue()
        {
            return Value;
        }

        public override void SetValue(object newValue)
        {
            Value = newValue;
        }

        public override void RemoveValue()
        {
            Value = null;
        }
    }


    public class PerRequestLifetimeModule : IHttpModule
    {
        private static readonly object Key = new object();
        private HttpContextBase _httpContext;

        public PerRequestLifetimeModule(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public PerRequestLifetimeModule()
        {
        }

        internal IDictionary<PerRequestLifetimeManager, object> Instances
        {
            get
            {
                _httpContext = (HttpContext.Current != null)
                                   ? new HttpContextWrapper(HttpContext.Current)
                                   : _httpContext;

                return (_httpContext == null) ? null : GetInstances(_httpContext);
            }
        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.EndRequest += (sender, e) => RemoveAllInstances();
        }

        void IHttpModule.Dispose()
        {
        }

        internal static IDictionary<PerRequestLifetimeManager, object> GetInstances(HttpContextBase httpContext)
        {
            IDictionary<PerRequestLifetimeManager, object> instances;

            if (httpContext.Items.Contains(Key))
            {
                instances = (IDictionary<PerRequestLifetimeManager, object>)httpContext.Items[Key];
            }
            else
            {
                lock (httpContext.Items)
                {
                    if (httpContext.Items.Contains(Key))
                    {
                        instances = (IDictionary<PerRequestLifetimeManager, object>)httpContext.Items[Key];
                    }
                    else
                    {
                        instances = new Dictionary<PerRequestLifetimeManager, object>();
                        httpContext.Items.Add(Key, instances);
                    }
                }
            }

            return instances;
        }

        internal void RemoveAllInstances()
        {
            IDictionary<PerRequestLifetimeManager, object> instances = Instances;

            if (instances != null)
            {
                foreach (var entry in instances)
                {
                    var disposable = entry.Value as IDisposable;

                    if (disposable != null)
                    {
                        disposable.Dispose();
                    }
                }

                instances.Clear();
            }
        }
    }
}